﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DataAccess = Microsoft.MSE.Catalog.Module.Data;
using DTO = Microsoft.MSE.Catalog.Contracts;
using System.Xml;
using System.Data.SqlClient;
using System.Data;

namespace Microsoft.MSE.Catalog.Module.Business
{
    public class SystemsManager
    {
        public SystemsManager()
        {

        }

        #region System functions

        public static Guid CreateSystem(DTO.System system, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.MSESystem.CreateSystem(system, txn, conn);
        }

        public static DTO.System GetSystem(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.MSESystem.GetSystem(ID, txn, conn);
        }

        public static void DeleteSystem(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.MSESystem.DeleteSystem(ID, txn, conn);
        }

        public static DTO.System UpdateSystem(DTO.System system, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            bool newTransaction = false;
            DTO.System result = null;

            Guid sysID = DataAccess.Entity.GetIDFromName(system.Name, Microsoft.MSE.Catalog.Contracts.EntityClass.System, txn, conn);
            if (sysID == Guid.Empty || (sysID.CompareTo(system.ID) == 0))
            {

                try
                {
                    if (conn == null) conn = CatalogSvcHelper.GetConnection();
                    if (conn.State == ConnectionState.Closed)
                    {
                        autoclose = true;
                        conn.Open();
                    }

                    if (txn == null)
                    {
                        txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);
                        newTransaction = true;
                    }

                    //Change Tracking
                    DTO.RepositoryChangeSet changeSet = ChangeTrackingHelper.GetChangeSetForSystemUpdate(system, txn, conn);

                    result = DataAccess.MSESystem.UpdateSystem(system, txn, conn);

                    //record ChangeSet
                    DataAccess.RepositoryChange.Save(changeSet, txn, conn);

                    if ((autoclose) && (txn != null) && (newTransaction)) txn.Commit();
                }
                catch (Exception)
                {
                    if ((txn != null) && (newTransaction)) txn.Rollback();
                    throw;
                }
                finally
                {
                    if (autoclose && (conn != null)) conn.Close();
                }
            }
            else
            {
                if (txn != null) txn.Rollback(); //Rollback transaction
                throw new ApplicationException("System already exists in the Service Model");
            }

            return result;
        }

        public static DTO.SystemList ListSystems(SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.MSESystem.ListSystems(txn, conn);
        }

        #endregion

        #region System Group functions

        public static List<DTO.SystemListElement> ListSystemsByGroup(Guid entityGroupID)
        {
            return DataAccess.MSESystem.ListSystemsByGroup(entityGroupID, null, null);
        }

        public static DTO.SystemGroup CreateSystemGroup(DTO.SystemGroup systemGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {
                    systemGroup.ID = DataAccess.Entity.CreateEntityGroup(systemGroup.Name, systemGroup.Description, 12, txn, conn);

                    if (systemGroup.Systems != null)
                    {
                        foreach (DTO.SystemListElement system in systemGroup.Systems)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(systemGroup.ID, system.ID, txn, conn);
                        }
                    }

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return systemGroup;
        }


        public static DTO.SystemGroup GetSystemGroup(Guid ID)
        {
            DTO.SystemGroup systemGroup = new DTO.SystemGroup();
            DTO.EntityGroup entityGroup = new DTO.EntityGroup();

            entityGroup = DataAccess.Entity.GetEntityGroup(ID, null, null);

            systemGroup.ID = entityGroup.ID;
            systemGroup.Name = entityGroup.Name;
            systemGroup.Description = entityGroup.Description;

            systemGroup.Systems = ListSystemsByGroup(ID);

            return systemGroup;
        }

        public static DTO.SystemGroup UpdateSystemGroup(DTO.SystemGroup systemGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {

                DataAccess.Entity.UpdateEntityGroup(systemGroup.ID, systemGroup.Name, systemGroup.Description, txn, conn);

                List<DTO.SystemListElement> oldList = DataAccess.MSESystem.ListSystemsByGroup(systemGroup.ID, txn, conn);
                /// Delete all items from the old list
                if (oldList != null)
                {
                    foreach (DTO.SystemListElement oldListElement in oldList)
                    {
                        DataAccess.Entity.RemoveEntityFromEntityGroup(systemGroup.ID, oldListElement.ID, txn, conn);
                    }
                }
                /// Add all items from the new list
                if (systemGroup.Systems != null)
                {
                    foreach (DTO.SystemListElement newListElement in systemGroup.Systems)
                    {
                        DataAccess.Entity.AssignEntityToEntityGroup(systemGroup.ID, newListElement.ID, txn, conn);
                    }
                }

                if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return systemGroup;
        }

        #endregion

        #region System Instance functions

        public static Guid CreateSystemInstance(DTO.SystemInstance systemInstance, SqlTransaction txn, SqlConnection conn)
        {
            string uri;
            if (systemInstance.SystemID != null && systemInstance.SystemID.CompareTo(Guid.Empty) != 0) uri = DataAccess.MSESystem.GetSystem(systemInstance.SystemID, txn, conn).URI + ":" + systemInstance.Port.ToString() + systemInstance.URI;
            else
            {
                uri = "";
                if (systemInstance.Port > 0)
                    uri += ":" + systemInstance.Port.ToString();
                uri += systemInstance.URI;
            }

            Guid siID = DataAccess.MSESystemInstance.GetIDForSystemInstance(systemInstance.Name, uri, txn, conn);
            if (siID == Guid.Empty)
            {
                //Note: No change tracking, a new system has not been assigned to a resource
                return DataAccess.MSESystemInstance.CreateSystemInstance(systemInstance, txn, conn);
            }
            else
            {
                if (txn != null) txn.Rollback(); //Rollback transaction
                throw new ApplicationException("System Instance already exists in the Service Model");
            }

        }

        public static DTO.SystemInstance UpdateSystemInstance(DTO.SystemInstance systemInstance, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            bool newTransaction = false;
            DTO.SystemInstance result = null;

            string uri;
            if (systemInstance.SystemID != null && systemInstance.SystemID.CompareTo(Guid.Empty) != 0) uri = DataAccess.MSESystem.GetSystem(systemInstance.SystemID, txn, conn).URI + ":" + systemInstance.Port.ToString() + systemInstance.URI;
            else
            {
                uri = "";
                if (systemInstance.Port > 0)
                    uri += ":" + systemInstance.Port.ToString();
                uri += systemInstance.URI;
            }

            Guid siID = DataAccess.MSESystemInstance.GetIDForSystemInstance(systemInstance.Name, uri, txn, conn);
            if (siID == Guid.Empty || (siID.CompareTo(systemInstance.ID) == 0))
            {
                try
                {
                    if (conn == null) conn = CatalogSvcHelper.GetConnection();
                    if (conn.State == ConnectionState.Closed)
                    {
                        autoclose = true;
                        conn.Open();
                    }

                    if (txn == null)
                    {
                        txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);
                        newTransaction = true;
                    }

                    //Change Tracking
                    DTO.RepositoryChangeSet changeSet = ChangeTrackingHelper.GetChangeSetForSystemInstanceUpdate(systemInstance, txn, conn);

                    result = DataAccess.MSESystemInstance.UpdateSystemInstance(systemInstance, txn, conn);

                    //record ChangeSet
                    DataAccess.RepositoryChange.Save(changeSet, txn, conn);

                    if ((autoclose) && (txn != null) && (newTransaction)) txn.Commit();
                }
                catch (Exception)
                {
                    if ((txn != null) && (newTransaction)) txn.Rollback();
                    throw;
                }
                finally
                {
                    if (autoclose && (conn != null)) conn.Close();
                }

            }
            else
            {
                if (txn != null) txn.Rollback(); //Rollback transaction
                throw new ApplicationException("System Instance already exists in the Service Model");
            }

            return result;
        }

        public static void DeleteSystemInstance(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.MSESystemInstance.DeleteSystemInstance(ID, txn, conn);
        }

        public static DTO.SystemInstance GetSystemInstance(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.MSESystemInstance.GetSystemInstance(ID, txn, conn);
        }

        public static DTO.SystemInstanceList ListSystemInstances(SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.MSESystemInstance.ListSystemInstances(txn, conn);
        }

        public static DTO.SystemInstanceList ListSystemInstancesForSystem(Guid systemID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.MSESystemInstance.ListSystemInstancesForSystem(systemID, txn, conn);
        }

        #endregion

        #region System Instance Group functions

        public static List<DTO.SystemInstanceListElement> ListSystemInstancesByGroup(Guid entityGroupID)
        {
            return DataAccess.MSESystemInstance.ListSystemInstancesByGroup(entityGroupID, null, null);
        }

        public static DTO.SystemInstanceGroup CreateSystemInstanceGroup(DTO.SystemInstanceGroup systemInstanceGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {
                    systemInstanceGroup.ID = DataAccess.Entity.CreateEntityGroup(systemInstanceGroup.Name, systemInstanceGroup.Description, 14, txn, conn);

                    if (systemInstanceGroup.SystemInstances != null)
                    {
                        foreach (DTO.SystemInstanceListElement systemInstance in systemInstanceGroup.SystemInstances)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(systemInstanceGroup.ID, systemInstance.ID, txn, conn);
                        }
                    }

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return systemInstanceGroup;
        }


        public static DTO.SystemInstanceGroup GetSystemInstanceGroup(Guid ID)
        {
            DTO.SystemInstanceGroup systemInstanceGroup = new DTO.SystemInstanceGroup();
            DTO.EntityGroup entityGroup = new DTO.EntityGroup();

            entityGroup = DataAccess.Entity.GetEntityGroup(ID, null, null);

            systemInstanceGroup.ID = entityGroup.ID;
            systemInstanceGroup.Name = entityGroup.Name;
            systemInstanceGroup.Description = entityGroup.Description;

            systemInstanceGroup.SystemInstances = ListSystemInstancesByGroup(ID);

            return systemInstanceGroup;
        }

        public static DTO.SystemInstanceGroup UpdateSystemInstanceGroup(DTO.SystemInstanceGroup systemInstanceGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {

                    DataAccess.Entity.UpdateEntityGroup(systemInstanceGroup.ID, systemInstanceGroup.Name, systemInstanceGroup.Description, txn, conn);

                    List<DTO.SystemInstanceListElement> oldList = DataAccess.MSESystemInstance.ListSystemInstancesByGroup(systemInstanceGroup.ID, txn, conn);
                    /// Delete all items from the old list
                    if (oldList != null)
                    {
                        foreach (DTO.SystemInstanceListElement oldListElement in oldList)
                        {
                            DataAccess.Entity.RemoveEntityFromEntityGroup(systemInstanceGroup.ID, oldListElement.ID, txn, conn);
                        }
                    }
                    /// Add all items from the new list
                    if (systemInstanceGroup.SystemInstances != null)
                    {
                        foreach (DTO.SystemInstanceListElement newListElement in systemInstanceGroup.SystemInstances)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(systemInstanceGroup.ID, newListElement.ID, txn, conn);
                        }
                    }

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return systemInstanceGroup;
        }

        #endregion

        #region Resource functions

        public static Guid CreateResource(DTO.Resource resource, SqlTransaction txn, SqlConnection conn)
        {
            string uri;
            Catalog.Contracts.SystemInstance SI;
            if (resource.InstanceID != null)
            {
                SI = DataAccess.MSESystemInstance.GetSystemInstance(resource.InstanceID, txn, conn);
                if (SI.SystemID != null)
                    uri = DataAccess.MSESystem.GetSystem(SI.SystemID, txn, conn).URI + ":" + SI.Port.ToString() + SI.URI + resource.URI;
                else
                    uri = ":" + SI.Port.ToString() + SI.URI + resource.URI;
            }
            else
                uri = resource.URI;


            Guid siID = DataAccess.Resource.GetIDForResource(resource.Name, CatalogSvcHelper.VerifyBinding(resource.Binding.BindingConfig), uri, txn, conn);
            if (siID == Guid.Empty)
            {
                //Note: No change tracking, a new system has not been assigned to a resource
                return DataAccess.Resource.CreateResource(resource, txn, conn);
            }
            else
            {
                if (txn != null) txn.Rollback(); //Rollback transaction
                throw new ApplicationException("Resource already exists in the Service Model");
            }

        }

        public static DTO.Resource GetResource(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Resource.GetResource(ID, false, null, txn, conn);
        }

        public static void DeleteResource(Guid ID, SqlTransaction txn, SqlConnection conn)
        {
            DataAccess.Resource.DeleteResource(ID, txn, conn);
        }

        public static DTO.Resource UpdateResource(DTO.Resource resource, SqlTransaction txn, SqlConnection conn)
        {
            bool autoclose = false;
            bool newTransaction = false;
            DTO.Resource result = null;

            string uri;
            Catalog.Contracts.SystemInstance SI;
            if (resource.InstanceID != null && resource.InstanceID.CompareTo(Guid.Empty) != 0)
            {
                SI = DataAccess.MSESystemInstance.GetSystemInstance(resource.InstanceID, txn, conn);
                if (SI.SystemID != null && SI.SystemID.CompareTo(Guid.Empty) !=0)
                    uri = DataAccess.MSESystem.GetSystem(SI.SystemID, txn, conn).URI + ":" + SI.Port.ToString() + SI.URI + resource.URI;
                else
                    uri = ":" + SI.Port.ToString() + SI.URI + resource.URI;
            }
            else
                uri = resource.URI;


            Guid resID = DataAccess.Resource.GetIDForResource(resource.Name, CatalogSvcHelper.VerifyBinding(resource.Binding.BindingConfig), uri, txn, conn);
            if (resID == Guid.Empty || (resID.CompareTo(resource.ID) == 0)) 
            {
                try
                {
                    if (conn == null) conn = CatalogSvcHelper.GetConnection();
                    if (conn.State == ConnectionState.Closed)
                    {
                        autoclose = true;
                        conn.Open();
                    }

                    if (txn == null)
                    {
                        txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);
                        newTransaction = true;
                    }

                    //Change Tracking
                    DTO.RepositoryChangeSet changeSet = ChangeTrackingHelper.GetChangeSetForResourceUpdate(resource, txn, conn);

                    result = DataAccess.Resource.UpdateResource(resource, txn, conn);

                    //record ChangeSet
                    DataAccess.RepositoryChange.Save(changeSet, txn, conn);

                    if ((autoclose) && (txn != null) && (newTransaction)) txn.Commit();
                }
                catch (Exception)
                {
                    if ((txn != null) && (newTransaction)) txn.Rollback();
                    throw;
                }
                finally
                {
                    if (autoclose && (conn != null)) conn.Close();
                }
            }
            else
            {
                if (txn != null) txn.Rollback(); //Rollback transaction
                throw new ApplicationException("Resource already exists in the Service Model");
            }

            return result;

        }
        public static DTO.ResourceList ListResources( SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Resource.ListResources(txn, conn);
        }

        public static DTO.ResourceList ListResourcesForSystemInstance(Guid InstanceID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Resource.ListResourcesForSystemInstance(InstanceID, txn, conn);
        }

        public static DTO.ResourceList ListResourcesForBindingID(Guid bindingID, SqlTransaction txn, SqlConnection conn)
        {
            return DataAccess.Resource.ListResourcesForBindingID(bindingID, txn, conn);
        }

        #endregion

        #region Resource Group functions

        public static DTO.ResourceGroup CreateResourceGroup(DTO.ResourceGroup resourceGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {
                    resourceGroup.ID = DataAccess.Entity.CreateEntityGroup(resourceGroup.Name, resourceGroup.Description, 16, txn, conn);

                    if (resourceGroup.Resources != null)
                    {
                        foreach (DTO.ResourceListElement system in resourceGroup.Resources)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(resourceGroup.ID, system.ID, txn, conn);
                        }
                    }

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return resourceGroup;
        }

        public static DTO.ResourceGroup GetResourceGroup(Guid ID)
        {
            DTO.ResourceGroup resourceGroup = new DTO.ResourceGroup();
            DTO.EntityGroup entityGroup = new DTO.EntityGroup();

            entityGroup = DataAccess.Entity.GetEntityGroup(ID, null, null);

            resourceGroup.ID = entityGroup.ID;
            resourceGroup.Name = entityGroup.Name;
            resourceGroup.Description = entityGroup.Description;

            resourceGroup.Resources = ListResourcesByGroup(ID);

            return resourceGroup;
        }

        public static DTO.ResourceGroup UpdateResourceGroup(DTO.ResourceGroup resourceGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {

                    DataAccess.Entity.UpdateEntityGroup(resourceGroup.ID, resourceGroup.Name, resourceGroup.Description, txn, conn);

                    List<DTO.ResourceListElement> oldList = DataAccess.Resource.ListResourcesByGroup(resourceGroup.ID, txn, conn);
                    /// Delete all items from the old list
                    if (oldList != null)
                    {
                        foreach (DTO.ResourceListElement oldListElement in oldList)
                        {
                            DataAccess.Entity.RemoveEntityFromEntityGroup(resourceGroup.ID, oldListElement.ID, txn, conn);
                        }
                    }
                    /// Add all items from the new list
                    if (resourceGroup.Resources != null)
                    {
                        foreach (DTO.ResourceListElement newListElement in resourceGroup.Resources)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(resourceGroup.ID, newListElement.ID, txn, conn);
                        }
                    }

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return resourceGroup;
        }

        public static List<DTO.ResourceListElement> ListResourcesByGroup(Guid entityGroupID)
        {
            return DataAccess.Resource.ListResourcesByGroup(entityGroupID, null, null);
        }

        #endregion
    }
}
